//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Builds the native `pkl` CLI
extends "GradleJob.pkl"

import "package://pkg.pkl-lang.org/pkl-pantry/com.circleci.v2@1.0.0#/Config.pkl"
import "package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.uri@1.0.0#/URI.pkl"

/// The OS to run on
os: "macOS"|"linux"

/// The architecture to use
arch: "amd64"|"aarch64"

/// Whether to link to musl. Otherwise, links to glibc.
musl: Boolean = false

local setupLinuxEnvironment: Config.RunStep =
  let (jdkVersion = "11.0.20.1+1")
  let (muslVersion = "1.2.2")
  let (zlibVersion = "1.2.13")
  let (jdkVersionEncoded = URI.encodeComponent(jdkVersion))
  let (jdkVersionAlt = jdkVersion.replaceLast("+", "_"))
  let (majorJdkVersion = jdkVersion.split(".").first)
    new {
      name = "Set up environment"
      shell = "#!/bin/bash -exo pipefail"
      command = new Listing {
        #"""
        sed -ie '/\[ol8_codeready_builder\]/,/^$/s/enabled=0/enabled=1/g' /etc/yum.repos.d/oracle-linux-ol8.repo \
          && microdnf -y install util-linux tree coreutils-single findutils curl tar gzip git zlib-devel gcc-c++ make openssl glibc-langpack-en libstdc++-static \
          && microdnf clean all \
          && rm -rf /var/cache/dnf

        # install jdk
        curl -L \
         https://github.com/adoptium/temurin\#(majorJdkVersion)-binaries/releases/download/jdk-\#(jdkVersionEncoded)/OpenJDK\#(majorJdkVersion)U-jdk_\#(if (arch == "amd64") "x64" else "aarch64")_linux_hotspot_\#(jdkVersionAlt).tar.gz -o /tmp/jdk.tar.gz

        mkdir /jdk \
          && cd /jdk \
          && cat /tmp/jdk.tar.gz | tar --strip-components=1 -xzC .

        mkdir -p ~/staticdeps/bin

        cp /usr/lib/gcc/\#(if (arch == "amd64") "x86_64" else "aarch64")-redhat-linux/8/libstdc++.a ~/staticdeps

        # install zlib
        if [[ ! -f ~/staticdeps/include/zlib.h ]]; then
          curl -L https://github.com/madler/zlib/releases/download/v\#(zlibVersion)/zlib-\#(zlibVersion).tar.gz -o /tmp/zlib.tar.gz

          mkdir -p /tmp/dep_zlib-\#(zlibVersion) \
          && cd /tmp/dep_zlib-\#(zlibVersion) \
          && cat /tmp/zlib.tar.gz | tar --strip-components=1 -xzC . \
          && echo "zlib-\#(zlibVersion): configure..." && ./configure --static --prefix="$HOME"/staticdeps > /dev/null \
          && echo "zlib-\#(zlibVersion): make..." && make -s -j4 \
          && echo "zlib-\#(zlibVersion): make install..." && make -s install \
          && rm -rf /tmp/dep_zlib-\#(zlibVersion)
        fi
        """#
        // don't need musl on aarch because GraalVM only supports musl builds on x86
        when (arch == "amd64") {
            #"""
            # install musl
            if [[ ! -f ~/staticdeps/bin/x86_64-linux-musl-gcc ]]; then
              curl -L https://musl.libc.org/releases/musl-\#(muslVersion).tar.gz -o /tmp/musl.tar.gz

              mkdir -p /tmp/dep_musl-\#(muslVersion) \
              && cd /tmp/dep_musl-\#(muslVersion) \
              && cat /tmp/musl.tar.gz | tar --strip-components=1 -xzC . \
              && echo "musl-\#(muslVersion): configure..." && ./configure --disable-shared --prefix="$HOME"/staticdeps > /dev/null \
              && echo "musl-\#(muslVersion): make..." && make -s -j4 \
              && echo "musl-\#(muslVersion): make install..." && make -s install \
              && rm -rf /tmp/dep_musl-\#(muslVersion)

              # native-image expects to find an executable at this path.
              ln -s ~/staticdeps/bin/musl-gcc ~/staticdeps/bin/x86_64-linux-musl-gcc
            fi
            """#
        }
      }.join("\n\n")
    }

steps {
  when (os == "linux") {
    new Config.RestoreCacheStep {
      name = "Restore static deps from cache"
      key = "staticdeps-\(arch)"
    }
    setupLinuxEnvironment
    new Config.SaveCacheStep {
      name = "Save statics deps to cache"
      key = "staticdeps-\(arch)"
      paths {
        "~/staticdeps"
      }
    }
  }
  when (os == "macOS" && arch == "amd64") {
    new Config.RunStep {
      name = "Installing Rosetta 2"
      command = """
        /usr/sbin/softwareupdate --install-rosetta --agree-to-license
        """
    }
  }
  // If building macOS/aarch64, we need to use GraalVM 23.
  // We can't use GraalVM 23 for any other build because we need to support Java 11, which was
  // dropped in GraalVM 23.
  when (os == "macOS" && arch == "aarch64") {
    new Config.RunStep {
      command = "git apply patches/graalVm23.patch"
    }
  }
  new Config.RunStep {
    name = "gradle buildNative"
    local _os =
      if (os == "macOS") "mac"
      else if (musl) "alpine"
      else "linux"
    local jobName = "\(_os)Executable\(arch.capitalize())"
    command = #"""
      export PATH=~/staticdeps/bin:$PATH
      ./gradlew \#(module.gradleArgs) pkl-cli:\#(jobName) pkl-core:test\#(jobName.capitalize())
      """#
  }
  new Config.PersistToWorkspaceStep {
    root = "."
    paths {
      "pkl-cli/build/executable/"
    }
  }
}

job {
  when (os == "macOS") {
    macos {
      xcode = "15.2.0"
    }
    // Use M1 for all architectures. We build amd64/aarch64 based on the GraalVM version,
    // which gets patched in via `git apply patches/graalVm23.patch`.
    resource_class = "macos.m1.large.gen1"
  } else {
    docker {
      new {
        image = if (arch == "aarch64") "arm64v8/oraclelinux:8-slim" else "oraclelinux:8-slim"
      }
    }
    environment {
      ["JAVA_HOME"] = "/jdk"
    }
    resource_class = if (arch == "aarch64") "arm.xlarge" else "xlarge"
  }
}
